package baseStructure;

/***
 * @author Ragnvald Fuglestein
 */

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import callback.ElementChanged;

@SuppressWarnings("rawtypes")
public abstract class Base implements Serializable, Comparable {
	private static final long serialVersionUID = -7910895892320080770L;
	
	private List<String> names = new ArrayList<>();
	protected HashMap<String,Addon> addons = new HashMap<>();
	
	protected ArrayList<ElementChanged> elementChangedListeners = new ArrayList<>();
	public void addElementChangedListener(ElementChanged listener) {
		elementChangedListeners.add(listener);
	}
	public boolean removeElementChangedListener(ElementChanged listener) {
		return elementChangedListeners.remove(listener);
	}
	
	protected void notifyElementChangedListener() {
		for (ElementChanged listener : elementChangedListeners) {
			listener.elementChanged(this);
		}
	}
	
	public Base(final String name) {
		names.add(name);
	}

	public final String getName() {
		return names.get(0);
	}
	
	public final void setName(String name) {
		names.remove(name);
		names.add(0, name);
		notifyElementChangedListener();
	}
	
	/***
	 * @param index of name to be retrieved from name list
	 */
	public final String getName(final int num) {
		return names.get(num);
	}
	
	/***
	 * @param index of name to be retrieved from name list
	 */
	public final List<String> getNames() {
		return names;
	}
	
	/***
	 * @param name to add to the end of the name list
	 */
	public final boolean addName(final String name) {
		if (names.contains(name)) return false;
		return names.add(name);
	}
	
	/***
	 * @param name to be removed from name list
	 */
	public final boolean removeName(final String name) {
		if (names.size() == 1) throw new RuntimeException("Tried to remove the only name: " + names.get(0));
		return names.remove(name);
	}
	
	/***
	 * 
	 * @param name
	 * @return true if name is recognized
	 */
	public final boolean hasName(final String name) {
		return names.indexOf(name) != -1;
	}
	
	/***
	 * 
	 * @param name
	 * @return true if name is recognized
	 */
	public final boolean hasNameCaseInsensitive(final String name) {
		for (String n : names) {
			if (n.compareToIgnoreCase(name) == 0) return true;
		}
		return false;
	}

	/***
	 * 
	 * @param search
	 * @return true if the extended search matches the object
	 */
	public final boolean matches(ExtendedSearch search) {
		boolean ret = false;
		Addon addon = addons.get(search.addon);
		if (addon != null) {
			if (search.key != null && !addon.checkField(search.key, search.value)) return false;
			ret = true;
		}
		return ret;
	}
	
	/***
	 * Addons addon to addonlist, or merge if it already exists
	 * @param addon
	 */
	public final void addAddon(Addon addon) {
		Addon a = addons.get(addon.name);
		if (a == null) {
			addons.put(addon.name, addon);
		} else a.merge(addon);
	}
	
	/***
	 * 
	 * @param name
	 * @return addon if it exists, or null
	 */
	public final Addon getAddon(String name) {
		return addons.get(name);
	}
	
	/***
	 * 
	 * @return hashmap of addons
	 */
	public final HashMap<String,Addon> getAddons() { return addons; }
	
	/***
	 * Merges valid names and addons from base
	 * @param base
	 * @return true if names were changed
	 */
	protected final boolean merge(Base base) {
		boolean changed = false;
		for (String name : base.getNames()) {
			if (addName(name)) changed = true;
		}
		
		for (Addon addon : base.getAddons().values()) {
			addAddon(addon);
		}
		
		return changed;
	}
	
	public String getInfo() {
		String ret = "";
		
		for (String Name : names) {
			if (!getName().equals(Name)) ret += "Alternative name: " + Name + "\n";
		}
		
		for (Addon addon : addons.values()) {
			ret += addon.getName() + ":\n" + addon.getInfo();
		}
		
		return ret;
	}
	
	@Override public int compareTo(Object o) {
		if (!(o instanceof Base)) throw new RuntimeException("base.compareTo");
		return toString().compareToIgnoreCase(o.toString());
	}
}
